/*
    Dijjer - A Peer to Peer HTTP Cache
    Copyright (C) 2004,2005  Change.Tv, Inc

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package dijjer;

import dijjer.io.comm.*;
import dijjer.io.xfer.*;
import dijjer.updater.*;
import dijjer.util.*;
import dijjer.util.logging.*;

import java.io.*;
import java.net.*;

public class Main {

	public static final String VERSION = "$Id: Main.java 40 2009-04-03 16:51:12Z mathias.demare $";

	private static File _dataDirectory = null;
	private static File _classDir = null;

	public static void main(String[] args) throws Exception {
		if ("Sun Microsystems Inc.".equals(System.getProperty("java.vm.vendor"))) {
			Logger.configure(args);
			AbstractFactory.configure(args);
			Loader.load(args);
		} else {
			start(args);
		}
	}

	public static void start(String[] args) throws Exception {
		AbstractFactory.configure(args);
		Logger.configure(args);
		Dijjer.init();
		createParser().parse(args);
		initDirs();
		Dijjer.getDijjer().start(args);
	}

	public static void shutdown(boolean exit) {
		Dijjer.getDijjer().shutdown(exit);
	}

	private static void initDirs() {
		if (_dataDirectory == null) {
			_dataDirectory = new File(System.getProperty("user.dir") + File.separator + ".dijjer");
		}
		if (!_dataDirectory.exists()) {
			_dataDirectory.mkdir();
		}
		if (_classDir == null) {
			_classDir = new File(_dataDirectory, "classes");
		}
		if (!_classDir.exists()) {
			_classDir.mkdir();
		}
		AbstractFactory.getFactory().getResourceManager().setClassCacheDirectory(_classDir);
		AbstractFactory.getFactory().getResourceManager().setDataDirectory(_dataDirectory);
	}

	private static ArgParser createParser() throws Exception {
		ArgParser parser = new ArgParser("dijjer", false) {
			{
				addArgument("localNetwork", "Allows Dijjer to communicate with LAN networks like 192.168.1.0/24", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						int slash = arg.indexOf('/');
						String localNetwork = arg.substring(0, slash);
						int cidr = Integer.parseInt(arg.substring(slash+1, arg.length()));
						if(slash != -1) {
							Dijjer dijj = Dijjer.getDijjer();
							if(AbstractFactory.getFactory().isSiteLocal(localNetwork)) {
								dijj.setUseLocalNetwork(true);
								dijj.setLocalNetwork(arg);
							}
							else
								Logger.warning("Non-local network given");
						}
						else
							Logger.warning("LocalNetwork is not given in the form address/cidr");
					}
				});
				addUnmatchedKeyArgument("neverUpdate", "Forces Dijjer to run in the default classloader, and will never perform auto updates", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Loader.setUpdateStrategy(Loader.NEVER);
					}
				});
				addArgument("update", "Update stategy, choices are: \"manual\" (must use localhost:port/update), "
				    + "\"good\" (only when good build is found), or \"new\" (any new build that is found)", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Loader.setUpdateStrategy(arg);
					}
				});
				addArgument("runtimeCache", "The directory in which Dijjer should cache new dijjer jars", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						_classDir = new File(arg);
					}
				});
				addArgument("dijjerURL", "The where newest builds of Dijjer can be located", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						AbstractFactory.getFactory().getResourceManager().setDownloadURL(arg);
					}
				});
				addArgument("preupdate", "Forces update of Dijjer code from specified location", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						String url = AbstractFactory.getFactory().getResourceManager().getDownloadURL();
						if (!"default".equalsIgnoreCase(arg)) {
							AbstractFactory.getFactory().getResourceManager().setDownloadURL(arg);
						}
						AbstractFactory.getFactory().getResourceManager().downloadCodeBase();
						AbstractFactory.getFactory().getResourceManager().setDownloadURL(url);
					}
				});
				addUnmatchedKeyArgument("versions", "Prints a list of all versions of all classes", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Version.printVersions();
						System.exit(0);
					}
				});
				addArgument("dataDirectory", "The directory in which Dijjer should cache data", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						_dataDirectory = new File(arg);
					}
				});
				addArgument("maxUpload", "The maximum upload bandwidth Dijjer should use (kbytes/sec)", new ArgumentAction() { 
					public void performAction(String arg) throws Exception {
						PacketThrottle.setMaxUploadBandwidth(Integer.parseInt(arg));
					}
				});
				addArgument("dataStoreSize", "The maximum size of Dijjer's cache in megabytes", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Dijjer.getDijjer().setDataStoreSize(Integer.parseInt(arg));
					}
				});
				addArgument("downloadThreads", "The maximum number of simultaneous downloads", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Dijjer.getDijjer().setDownloadThreadCount(Integer.parseInt(arg));
					}
				});
				addArgument("externalListenPort",
						"The UDP port on which Dijjer should listen for messages from other Dijjer peers",
				new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Dijjer.getDijjer().setExternalListenPort(Integer.parseInt(arg));
					}
				});
				addArgument("localListenPort", "The TCP port on which Dijjer should listen for local HTTP connections",
				new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Dijjer.getDijjer().setLocalListenPort(Integer.parseInt(arg));
					}
				});
				addArgument("routingTableSize", "The maximum number of peers Dijjer should retain connections to",
				new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Dijjer.getDijjer().setRoutingTableSize(Integer.parseInt(arg));
					}
				});
				addUnmatchedKeyArgument("disableRapidPing",
				"The peer will not rapidly ping peers that appear to be down", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						Dijjer.getDijjer().setDisableRapidPing(true);
					}
				});
				addArgument("seedNode", "The addresses of an seednode through which Dijjer can connect to the network",
				new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						if (!arg.equals("none")) {
							Dijjer.getDijjer().getSeedNodes().clear();
							AbstractPeer [] peers = AbstractFactory.getFactory().getPeers(arg, ",");
							for(int i=0; i<peers.length;i++) {
								if(peers[i] != null)
									Dijjer.getDijjer().addSeedNode(peers[i]);
							}
						} else {
                            Dijjer.getDijjer().setRoutingTableSize(Dijjer.SEED_NODE_ROUTING_TABLE_SIZE);
							Dijjer.getDijjer().getSeedNodes().clear();
						}
					}
				});
				addArgument("initPeer", "The addresses of some known peers, mainly for testing",
						new ArgumentAction() {
							public void performAction(String arg) throws Exception {
								Dijjer.getDijjer().getSeedNodes().clear();
								AbstractPeer [] peers = AbstractFactory.getFactory().getPeers(arg, ",");
								for(int i=0; i<peers.length;i++) {
									if(peers[i] != null)
										Dijjer.getDijjer().addPeer(peers[i]);
								}
							}
						});
				addUnmatchedKeyArgument("help", "This help menu", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						printValidArguments();
						System.exit(0);
					}
				});
                addArgument("allow", "A comma seperated list of DNS names or IP addresses to allow access to the status page", new ArgumentAction() {
                    public void performAction(String arg) throws Exception
                    {
                        String[] addresses = arg.split(",");
                        for (int i = 0; i < addresses.length; i++) {
                            String address = addresses[i];
                            AbstractFactory.getFactory().getResourceManager().addValidAddress(address);
                        }
                    }
                });
				addArgument("dumpMessageWaitTimes", "File to dump message wait times to (useful for tuning timeouts)", new ArgumentAction() {
					public void performAction(String arg) throws Exception {
						File f = new File(arg);
						if (!f.exists()) {
							f.createNewFile();
						}
						Dijjer.getDijjer().setDumpMessageWaitTimes(new PrintWriter(new FileOutputStream((f))));
						Dijjer.getDijjer().getDumpMessageWaitTimes().println("# Message Type\tTimeout (ms)\tActual Time (ms)");
					}
				});
				
			}
		};
		return parser;
	}

}
